#!/usr/bin/perl

package OutputManager;
use strict;
use warnings;
use Switch;

use constant FILE_OUTPUT => "1";
use constant STDOUT_OUTPUT => "2";

sub new {
	my $class = shift;
    my $self = {
    	_processManager	=> shift,
        _outputType	=> shift,
        _printMatrix => {},
        _orderedKeys => [],
        _readyList => [],
        _bloquedList => [],
        _executingList1 => [],
        _executingList2 => [],
    };
         
    bless $self, $class;
    return $self;
}

sub start{
	my( $self ) = @_;
	my $key;
	my $value;
	while (($key, $value) = each $self->{_processManager}->getProcesses()){
		if($value->hasUltsNew()){
			my $ult;
			foreach $ult (@{$value->getUltsNew()}){
				$self->{_printMatrix}{$ult->getName()} = [];
				push $self->{_orderedKeys}, $ult->getName();
			}
		}else{
			$self->{_printMatrix}{$value->getName()} = [];
			push $self->{_orderedKeys}, $value->getName();	
		}
	}
	
	$self->{_printMatrix}{"io1"} = [];
	$self->{_printMatrix}{"io2"} = [];
	$self->{_printMatrix}{"io3"} = [];
	$self->{_printMatrix}{"os"} = [];
	
	@{$self->{_orderedKeys}} = sort @{$self->{_orderedKeys}};
	push $self->{_orderedKeys}, "io1";
	push $self->{_orderedKeys}, "io2";
	push $self->{_orderedKeys}, "io3";
	push $self->{_orderedKeys}, "os";
	
	if($self->{_outputType} eq FILE_OUTPUT){
		open(FH,">".'output') or die "$!";
		close FH;
	}
	
	$self->{_processManager}->setOutput($self);
	$self->{_processManager}->process();
}

sub print{
	my( $self, $scheduler ) = @_;
	my $key;
	my $value;
	my $tick = 0;
	while (($key, $value) = each $self->{_printMatrix})
	{
		my $procName = "";
		my $procName2 = "";
		if($scheduler->getExecuting()){
			$procName = $scheduler->getExecuting()->getName();
			if($scheduler->getExecuting()->hasUlts()){
				$procName = $scheduler->getExecuting()->{_ults}[0]->getName();
			}
			
		}
		if($scheduler->getExecuting2()){
			$procName2 = $scheduler->getExecuting2()->getName();
			if($scheduler->getExecuting2()->hasUlts()){
				$procName2 = $scheduler->getExecuting2()->{_ults}[0]->getName();
			}
						
		}
		if($key eq $procName || $key eq $procName2){
			push $self->{_printMatrix}{$key},"X\t";
		}else{
			my $io1Size = @{$scheduler->getIO1List()};
			my $io2Size = @{$scheduler->getIO2List()};
			my $io3Size = @{$scheduler->getIO3List()};
			
			if(($key eq "io1") && $io1Size){
				push $self->{_printMatrix}{$key}, $scheduler->getIO1()->getName()."\t";
			}elsif(($key eq "io2") && $io2Size){
				push $self->{_printMatrix}{$key}, $scheduler->getIO2()->getName()."\t";
			}elsif(($key eq "io3") && $io3Size){
				push $self->{_printMatrix}{$key}, $scheduler->getIO3()->getName()."\t";
			}elsif($key eq "os" && $scheduler->isRunningOS()){
				push $self->{_printMatrix}{$key}, "X\t";
			}else{
				push $self->{_printMatrix}{$key},"-\t";
			}
		}
	}
	
	my $process;
	foreach $process (@{$scheduler->{_ready}}){
		$self->putIntoReady($process);
	}
	foreach $process (@{$scheduler->{_IO1}}){
		$self->putIntoBlocked($process);
	}
	foreach $process (@{$scheduler->{_IO2}}){
		$self->putIntoBlocked($process);
	}
	foreach $process (@{$scheduler->{_IO3}}){
		$self->putIntoBlocked($process);
	}
	if($scheduler->getExecuting()){
		$self->putIntoExecuting1($scheduler->getExecuting());
	}
	if($scheduler->getExecuting2()){
		$self->putIntoExecuting2($scheduler->getExecuting2());
	}
		
	$tick = @{$self->{_printMatrix}{"os"}};
	$tick--;
	switch($self->{_outputType}){
		case FILE_OUTPUT {_printToFile($self, $tick);}
		case STDOUT_OUTPUT {_printToSTDOUT($self,$tick);}
	}
}

sub getProcessName(){
	my ($self, $process) = @_;
	my @lst = split('_', $process->getName());
	
	return $lst[0];
}

sub putIntoReady{
	my ($self, $process) = @_;
	my $procName = $self->getProcessName($process);
	#chequeo si el ultimo es igual al que estoy tratando de meter
	my $size = @{$self->{_readyList}};
	if($size>0){
		my $lastProcess = $self->{_readyList}[$size-1];
		if($lastProcess && ($lastProcess ne $procName)){
			push $self->{_readyList}, $procName;
		}
	}else{
		push $self->{_readyList}, $procName;
	}
}

sub putIntoBlocked{
	my ($self, $process) = @_;
	my $procName = $self->getProcessName($process);
	#chequeo si el ultimo es igual al que estoy tratando de meter
	my $size = @{$self->{_bloquedList}};
	if($size){
		my $lastProcess = $self->{_bloquedList}[$size-1];
		if($lastProcess && ($lastProcess ne $procName)){
			push $self->{_bloquedList}, $procName;
		}
	}else{
		push $self->{_bloquedList}, $procName;
	}
}

sub putIntoExecuting1{
	my ($self, $process) = @_;
	my $procName = $self->getProcessName($process);
	#chequeo si el ultimo es igual al que estoy tratando de meter
	my $size = @{$self->{_executingList1}};
	if($size>0){
		my $lastProcess = $self->{_executingList1}[$size-1];
		if($lastProcess && ($lastProcess ne $procName)){
			push $self->{_executingList1}, $procName;
		}
	}else{
		push $self->{_executingList1}, $procName;
	}
}

sub putIntoExecuting2{
	my ($self, $process) = @_;
	my $procName = $self->getProcessName($process);
	#chequeo si el ultimo es igual al que estoy tratando de meter
	my $size = @{$self->{_executingList2}};
	if($size>0){
		my $lastProcess = $self->{_executingList2}[$size-1];
		if($lastProcess && ($lastProcess ne $procName)){
			push $self->{_executingList2}, $procName;
		}
	}else{
		push $self->{_executingList2}, $procName;
	}
}

sub _printToFile{
	my ($self, $tick) = @_;
	my $txt = _getToPrint($self,$tick);
	open(FH,">>".'output') or die "$!";
	print FH $txt;
	close FH;
}

sub _printToSTDOUT{
	my ($self, $tick) = @_;
	print $self->_getToPrint($tick)
}

sub _getToPrint{
	my ($self, $tick) = @_;
	my $key;
	my $value;
	
	my $doble = " \t";
	my $simple = "\t";
	
	my $str = "<--\n";
	
	$str .= "-- \t";
	for(my $i=0; $i<=$tick; $i++){
		if($i>99){
			$str .= $i;
		}elsif($i<10){
			$str .= $i.$doble;
		}else{
			$str .= $i.$simple;	
		}
		
	}
	$str .= "\n";
	foreach(@{$self->{_orderedKeys}}){
		my $key = $_;
		my $value = $self->{_printMatrix}{$key};
		$str .= $key."\t";
		for(@{$value}){
			$str .= $_." ";
		}
		$str .= "\n";
	}
	
	$str.="\nHistorico cola de listos:\n";
	my $name;
	my $names = "";
	foreach $name(@{$self->{_readyList}}){
		$names.=$name.", ";
	}
	$names = substr $names, 0, -2;
	$str .= $names;
	
	$str.="\nHistorico cola de bloqueados:\n";
	$names = "";
	foreach $name(@{$self->{_bloquedList}}){
		$names.=$name.", ";
	}
	$names = substr $names, 0, -2;
	$str .= $names;
	
	$str.="\nHistorico ejecutandose (nucleo 1):\n";
	$names = "";
	foreach $name(@{$self->{_executingList1}}){
		$names.=$name.", ";
	}
	$names = substr $names, 0, -2;
	$str .= $names;
	
	$str.="\nHistorico ejecutandose (nucleo 2):\n";
	$names = "";
	foreach $name(@{$self->{_executingList2}}){
		$names.=$name.", ";
	}
	$names = substr $names, 0, -2;
	$str .= $names."\n";
	
	$str .= "--/>\n";
	
	return $str;
}
1;